---
title: Operations
summary: WebMethod catalog for ADUserServices — the SOAP operation surface published by clsADUserServices.
status: draft
owner: Not determined from source
last_reviewed: 2026-09-22
---

# Operations

This host publishes exactly one ASMX service class, `ADUserServices.clsADUserServices`, exposing 19 `<WebMethod()>` operations for Active Directory user, group, and access-control maintenance across the internal (`corp.saif.com`) and external (`external.saif.com`) domains. There is no dispatcher, handler, or delegation layer: each operation is a direct method on the codebehind class, and no operation name is ever overridden — every wire operation name equals its VB method name.

This page is the operation catalog and hub. Each service class's operations are documented in detail on its own page under `contracts/`.

## Operation surface derivation

| Aspect | Finding |
| --- | --- |
| Authoritative surface | `<WebMethod()>` members on `ADUserServices.clsADUserServices` |
| SOAP namespace | `http://tempuri.org/` (`clsADUserServices.asmx.vb` line 38 — `<System.Web.Services.WebService(Namespace:="http://tempuri.org/")>`) |
| Operation naming | Method name — no `WebMethod(MessageName:="<name>")` override appears anywhere in the file |
| Binding conformance | `WsiProfiles.BasicProfile1_1` (`clsADUserServices.asmx.vb` line 39 — `<System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)>`) |
| Metadata document | Framework-generated, retrievable at `<service-address>?WSDL`; no WSDL is checked into the repository |

**Contract-stability finding.** The SOAP namespace is the untouched Visual Studio/framework template default, never changed to an organization- or service-specific value. This is a **HIGH**-severity contract-stability finding — see [tech-debt.md](tech-debt.md) TD-01 — because the WSDL target namespace, every message's XML namespace, and every operation's SOAP action all derive from this value, and it is shared with every other unmodified ASMX service. Do not normalize or "correct" this value anywhere in this document set; it is reported exactly as written in source. The SOAP action pattern is `<namespace><OperationName>`, e.g. `http://tempuri.org/GetUserEmail`. The deployed host or virtual path is not determinable from the repository — `<IISUrl>` is empty and `<UseIIS>False</UseIIS>` in `ADUserServices.vbproj`*.

`ScriptService` is **commented out and inactive** — line 37 reads `' <System.Web.Script.Services.ScriptService()> _`, a VB.NET line comment, not a live attribute. There is no JSON/AJAX-callable surface and no `system.web.extensions` registration anywhere in `Web.config`. Every one of the 19 `<WebMethod()>` occurrences is the bare attribute with no arguments — no `MessageName`, `Description`, `EnableSession`, `CacheDuration`, `BufferResponse`, or `TransactionOption` is supplied anywhere. Consequences of this bare usage: `EnableSession` defaults to `False` (the inherited `Session` property is not session-backed for any operation), `CacheDuration` defaults to `0` (no framework response caching), and there is no authored `Description` text carried into the WSDL — every operation summary in this document set is derived from the implementation.

WS-I Basic Profile 1.1 is a *declaration*, not a fully enforced constraint: two constructs on this contract sit uncomfortably with it — the untyped `ArrayList` returns on `GetUserGroups`/`GetUserResources`, which serialize as unconstrained `xsd:anyType` arrays (see [tech-debt.md](tech-debt.md) TD-05), and the `ADServiceResponse`/`HasAccessResponse` message types, whose public **fields** (not properties) form the message body. `EmitConformanceClaims` is not supplied on the `WebServiceBinding` attribute, so the framework default `False` applies and no conformance claim is emitted into the generated WSDL. The base class is `System.Web.Services.WebService` (`clsADUserServices.asmx.vb` line 42).

## Service class catalog

| Service class | `.asmx` file | SOAP namespace | Operations | Documentation |
| --- | --- | --- | --- | --- |
| `ADUserServices.clsADUserServices` | `clsADUserServices.asmx` | `http://tempuri.org/` | 19 | [Details](contracts/clsaduserservices.md) |

The marker file is `src\ADUserServices\clsADUserServices.asmx`; the codebehind is `src\ADUserServices\clsADUserServices.asmx.vb`.

## Operation catalog

19 `<WebMethod()>` members compose the operation surface. Eight are gated by the application-layer IP allow-list (`CheckAccess().HasAccess`); eleven have no access check at all — see [authentication-authorization.md](authentication-authorization.md).

| Operation | Service class | Input message | Output message | Faults | Auth | Rules |
| --- | --- | --- | --- | --- | --- | --- |
| [`HelloWorld`](contracts/clsaduserservices.md#helloworld) | `clsADUserServices` | None | `String` | None declared — cannot fail | None in code | — |
| [`AddUser`](contracts/clsaduserservices.md#adduser) | `clsADUserServices` | 13 `String` parts | `ADServiceResponse` | Sentinel `Failure` \| `SoapException` outside the two `Try` blocks | [Access-checked](authentication-authorization.md#sec-svc-005-checkaccess-itself-is-published-and-unauthenticated) | [BR-01](business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`UnlockAccount`](contracts/clsaduserservices.md#unlockaccount) | `clsADUserServices` | `inUserId` | `ADServiceResponse` | Sentinel `Failure` only — never faults* | [Access-checked](authentication-authorization.md#sec-svc-005-checkaccess-itself-is-published-and-unauthenticated) | [BR-01](business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`AddUserToGroup`](contracts/clsaduserservices.md#addusertogroup) | `clsADUserServices` | `inUserId`, `inGroupName` | `ADServiceResponse` | Sentinel `Failure`/`UserDoesNotExist`/`GroupDoesNotExist`/`UserAlreadyInGroup` — never faults | [Access-checked](authentication-authorization.md#sec-svc-005-checkaccess-itself-is-published-and-unauthenticated) | [BR-01](business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`DeleteUser`](contracts/clsaduserservices.md#deleteuser) | `clsADUserServices` | `inUserId` | `ADServiceResponse` | Sentinel `Failure` only — never faults | [Access-checked](authentication-authorization.md#sec-svc-005-checkaccess-itself-is-published-and-unauthenticated) | [BR-01](business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`DeleteUserFromGroup`](contracts/clsaduserservices.md#deleteuserfromgroup) | `clsADUserServices` | `inUserId`, `inGroupName` | `ADServiceResponse` | Sentinel `Success`/`GroupDoesNotExist` — directory failures are caught and swallowed (see [faults.md](faults.md)) | [Access-checked](authentication-authorization.md#sec-svc-005-checkaccess-itself-is-published-and-unauthenticated) | [BR-01](business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`DoesUserExist`](contracts/clsaduserservices.md#doesuserexist) | `clsADUserServices` | `inUserId`, `inExternalDomain` | `Boolean` | Never faults — `False` for absent user and for directory failure alike | [None in code](authentication-authorization.md) | [BR-06](business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`DoesUserHaveResource`](contracts/clsaduserservices.md#doesuserhaveresource) | `clsADUserServices` | `inUserId`, `inResourceName`, `inExternalDomain` | `ADServiceResponse` | `UserDoesNotExist` \| `SoapException` — no `Try`/`Catch` | [None in code](authentication-authorization.md) | [BR-06](business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`ChangeUserAccount`](contracts/clsaduserservices.md#changeuseraccount) | `clsADUserServices` | 10 `String` parts | `ADServiceResponse` | Sentinel `Failure`/`UserDoesNotExist` — never faults | [Access-checked](authentication-authorization.md#sec-svc-005-checkaccess-itself-is-published-and-unauthenticated) | [BR-01](business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`ChangeUserPassword`](contracts/clsaduserservices.md#changeuserpassword) | `clsADUserServices` | `inUserId`, `inNewPassword` | `ADServiceResponse` | Sentinel `Failure` only — never faults | [Access-checked](authentication-authorization.md#sec-svc-005-checkaccess-itself-is-published-and-unauthenticated) | [BR-01](business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`ChangeUserPasswordOrGetException`](contracts/clsaduserservices.md#changeuserpasswordorgetexception) | `clsADUserServices` | `inUserId`, `inNewPassword` | `ADServiceResponse` | `SoapException` — deliberately faulting variant, no `Try`/`Catch` | [Access-checked](authentication-authorization.md#sec-svc-005-checkaccess-itself-is-published-and-unauthenticated) | [BR-01](business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`GetUserEmail`](contracts/clsaduserservices.md#getuseremail) | `clsADUserServices` | `inUserId`, `inExternalDomain` | `ADServiceResponse` | Sentinel `Failure`/`UserDoesNotExist` — never faults | [None in code](authentication-authorization.md) | [BR-06](business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`GetUserGroups`](contracts/clsaduserservices.md#getusergroups) | `clsADUserServices` | `inUserId`, `inExternalDomain` | `ArrayList` | Never faults — empty `Catch` returns empty list on any failure | [None in code](authentication-authorization.md) | [BR-06](business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`GetUserResources`](contracts/clsaduserservices.md#getuserresources) | `clsADUserServices` | `inUserId`, `inProdEnvironment`, `inExternalDomain` | `ArrayList` | Never faults — empty `Catch` returns empty list on any failure | [None in code](authentication-authorization.md) | [BR-06](business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`GetUserFAX`](contracts/clsaduserservices.md#getuserfax) | `clsADUserServices` | `inUserId`, `inExternalDomain` | `ADServiceResponse` | Sentinel `Failure`/`UserDoesNotExist` — never faults | [None in code](authentication-authorization.md) | [BR-06](business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`GetUserLastLogon`](contracts/clsaduserservices.md#getuserlastlogon) | `clsADUserServices` | `inUserId`, `inExternalDomain` | `ADServiceResponse` | Sentinel `Failure`/`UserDoesNotExist`/`InfoNotAvailable` — never faults | [None in code](authentication-authorization.md) | [BR-06](business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`GetUserPrinter`](contracts/clsaduserservices.md#getuserprinter) | `clsADUserServices` | `inUserId` | `ADServiceResponse` | Sentinel `Failure`/`UserDoesNotExist` — never faults; internal domain only, no domain parameter offered | [None in code](authentication-authorization.md) | [BR-06](business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled) |
| [`IsUserInGroup`](contracts/clsaduserservices.md#isuseringroup) | `clsADUserServices` | `inUserId`, `inGroupName`, `inExternalDomain` | `ADServiceResponse` | Sentinel `Failure`/`UserDoesNotExist`/`GroupDoesNotExist` — never faults | [None in code](authentication-authorization.md) | [BR-06](business-rules.md#br-06-read-only-directory-queries-are-not-access-controlled), [BR-07](business-rules.md#br-07-the-directory-domain-is-selected-by-the-caller) |
| [`CheckAccess`](contracts/clsaduserservices.md#checkaccess) | `clsADUserServices` | None | `HasAccessResponse` | `SoapException` — no `Try`/`Catch` | [None in code](authentication-authorization.md) — is itself the access-control primitive | [BR-02](business-rules.md#br-02-a-local-request-is-always-approved), [BR-03](business-rules.md#br-03-an-allow-listed-source-address-is-approved), [BR-04](business-rules.md#br-04-the-caller's-source-address-prefers-the-forwarded-for-header) |

## Dispatch diagram

The following diagram visualizes how a caller reaches a handling operation.

```mermaid
flowchart TD
    REQ["SOAP request"] --> ASMX{"Marker file match<br>clsADUserServices.asmx"}
    ASMX -->|"No match"| NF["No listener at address<br>see faults.md"]
    ASMX -->|"Match"| OPSEL{"Operation selected<br>by SOAP action"}
    OPSEL -->|"Unknown action"| ACTFAULT["SOAP fault<br>method not recognized"]
    OPSEL -->|"8 gated operations"| GATE{"CheckAccess<br>HasAccess?"}
    OPSEL -->|"11 ungated operations"| OP["Operation body executes"]
    GATE -->|"No"| DENY["ADServiceResponse<br>Failure / Access Denied"]
    GATE -->|"Yes"| OP
    OP --> SENTINEL["Sentinel response<br>ADServiceResponse / Boolean / ArrayList"]
    OP --> FAULT["SoapException<br>see faults.md"]
```

## Operations by process

Operations that delegate to a documented processing workflow link to it here.

| Operation | Process |
| --- | --- |
| [`HelloWorld`](contracts/clsaduserservices.md#helloworld) | [OP-01](operations/hello-world.md) |
| [`AddUser`](contracts/clsaduserservices.md#adduser) | [OP-02](operations/add-user.md) |
| [`UnlockAccount`](contracts/clsaduserservices.md#unlockaccount) | [OP-03](operations/unlock-account.md) |
| [`AddUserToGroup`](contracts/clsaduserservices.md#addusertogroup) | [OP-04](operations/add-user-to-group.md) |
| [`DeleteUser`](contracts/clsaduserservices.md#deleteuser) | [OP-05](operations/delete-user.md) |
| [`DeleteUserFromGroup`](contracts/clsaduserservices.md#deleteuserfromgroup) | [OP-06](operations/delete-user-from-group.md) |
| [`DoesUserExist`](contracts/clsaduserservices.md#doesuserexist) | [OP-07](operations/does-user-exist.md) |
| [`DoesUserHaveResource`](contracts/clsaduserservices.md#doesuserhaveresource) | [OP-08](operations/does-user-have-resource.md) |
| [`ChangeUserAccount`](contracts/clsaduserservices.md#changeuseraccount) | [OP-09](operations/change-user-account.md) |
| [`ChangeUserPassword`](contracts/clsaduserservices.md#changeuserpassword) | [OP-10](operations/change-user-password.md) |
| [`ChangeUserPasswordOrGetException`](contracts/clsaduserservices.md#changeuserpasswordorgetexception) | [OP-11](operations/change-user-password-or-get-exception.md) |
| [`GetUserEmail`](contracts/clsaduserservices.md#getuseremail) | [OP-12](operations/get-user-email.md) |
| [`GetUserGroups`](contracts/clsaduserservices.md#getusergroups) | [OP-13](operations/get-user-groups.md) |
| [`GetUserResources`](contracts/clsaduserservices.md#getuserresources) | [OP-14](operations/get-user-resources.md) |
| [`GetUserFAX`](contracts/clsaduserservices.md#getuserfax) | [OP-15](operations/get-user-fax.md) |
| [`GetUserLastLogon`](contracts/clsaduserservices.md#getuserlastlogon) | [OP-16](operations/get-user-last-logon.md) |
| [`GetUserPrinter`](contracts/clsaduserservices.md#getuserprinter) | [OP-17](operations/get-user-printer.md) |
| [`IsUserInGroup`](contracts/clsaduserservices.md#isuseringroup) | [OP-18](operations/is-user-in-group.md) |
| [`CheckAccess`](contracts/clsaduserservices.md#checkaccess) | [OP-19](operations/check-access.md) |

## Coverage

| Check | Result |
| --- | --- |
| Service classes with a `.asmx` marker file | 1 |
| Service classes with a `contracts/` page | 1 |
| `<WebMethod()>` members across all service classes | 19 |
| Rows in the operation catalog | 19 |

## Related documentation
- [Service index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Activation and hosting](activation-hosting.md)
- [Faults and error pipeline](faults.md)
- [Authentication and authorization](authentication-authorization.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Operational runbook](operational-runbook.md)
- [Tech debt](tech-debt.md)

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with ** could not be confirmed; treat as unknown until verified.

## Sources
- `src\ADUserServices\clsADUserServices.asmx`
- `src\ADUserServices\clsADUserServices.asmx.vb`
- `.saifdocgen\state\runs\20260922-101326-aduserservices\analysis\aduserservices-analysis.md`
- `.saifdocgen\state\runs\20260922-101326-aduserservices\analysis\aduserservices-security-analysis.md`
